#ifndef CNC_BASE_CNC_MODE_H
#define CNC_BASE_CNC_MODE_H

#include "kernelsetup.h"
#include "uiservs.h"
#include "keyboard.h"
#include "machop.h"
#include "vkeyboard.h"
#ifdef _DEBUG
#include <iostream> // debug
#endif
#include <map>

namespace cnc {

	namespace base {

		class CNCMode : public cnc::base::CooperativeMode
		{
			CNCMode &operator=(CNCMode &); // copying disabled
		protected:
			std::map<int, cnc::base::OperationModeID> opMode_;
			const cnc::base::OperationModeID thisMode_;
			cnc::base::KernelSetup *coordinator_;
			cnc::ui::Services *uiService_;
			cnc::comm::Keyboard *keyboard_;
			cnc::base::MachineOperation *machOp_;
		public:
			CNCMode(cnc::base::OperationModeID thisMode) : thisMode_(thisMode), coordinator_(0),
				uiService_(0)
			{
				opMode_[cnc::comm::MODE::MEMORY] = cnc::base::OPMODE_MEMORY;
				opMode_[cnc::comm::MODE::MDI] = cnc::base::OPMODE_MDI;
				opMode_[cnc::comm::MODE::FILE_MANAGER] = cnc::base::OPMODE_TAPE;
				opMode_[cnc::comm::MODE::EDITOR] = cnc::base::OPMODE_EDIT;
				opMode_[cnc::comm::MODE::HANDWHEEL] = cnc::base::OPMODE_HANDWHEEL;
				opMode_[cnc::comm::MODE::JOG] = cnc::base::OPMODE_JOG;
				opMode_[cnc::comm::MODE::RAPID] = cnc::base::OPMODE_RAPID;
				opMode_[cnc::comm::MODE::HOME] = cnc::base::OPMODE_SETUP;
				opMode_[cnc::comm::MODE::END] = cnc::base::OPMODE_END;
			}
			void setCoordinator(cnc::base::KernelSetup *coordinator)
			{
				coordinator_ = coordinator;
			}
			void setUIServices(cnc::ui::Services *uiService)
			{
				uiService_ = uiService;
			}
			void setKeyboard(cnc::comm::Keyboard *keyboard)
			{
				keyboard_ = keyboard;
			}
			void setMachineOperation(cnc::base::MachineOperation *machOp)
			{
				machOp_ = machOp;
			}
			bool selectMode(cnc::base::OperationModeID nextMode)
			{
				if (coordinator_ == 0)
					return true;
				if (nextMode != thisMode_) {
					if (nextMode != cnc::base::OPMODE_END)
						coordinator_->selectMode(nextMode);
					return false;
				}
				return true;
			}
		#ifdef _DEBUG
			void printCommand(const cnc::comm::KbdCommand &kbdCmd)
			{
				static char *idName[] = {
					"Character",
					"Keystroke",
					"Emergency",
					"Cycle",
					"Mode",
					"Option",
					"Feedrate Override",
					"Jog Feedrate",
					"Rapid Feedrate Override",
					"Axis",
					"Spindle",
					"Spindle Speed",
					"Spindle Override",
					"Handwheel",
					"Tool",
					"Device"
				};
				if (kbdCmd.getID() == 0xFFFF)
					return;
				std::cout << idName[kbdCmd.getID()] << " = ";
				switch (kbdCmd.getID()) {
				case cnc::comm::CHARACTER::ID:
					std::cout << "'" << static_cast<char>(kbdCmd.getValue())
						<< "'" << std::endl;
					break;
				case cnc::comm::KEYSTROKE::ID:
					std::cout << kbdCmd.getValue() << std::endl;
					break;
				case cnc::comm::EMERGENCY::ID:
					switch (kbdCmd.getValue()) {
					case cnc::comm::EMERGENCY::STOP:
						std::cout << "Stop" << std::endl;
						break;
					case cnc::comm::EMERGENCY::CONTINUE:
						std::cout << "Continue" << std::endl;
						break;
					}
					break;
				case cnc::comm::CYCLE::ID:
					switch (kbdCmd.getValue()) {
					case cnc::comm::CYCLE::START:
						std::cout << "Start" << std::endl;
						break;
					case cnc::comm::CYCLE::FEED_HOLD:
						std::cout << "Feed hold" << std::endl;
						break;
					}
					break;
				case cnc::comm::MODE::ID:
					switch (kbdCmd.getValue()) {
					case cnc::comm::MODE::MEMORY:
						std::cout << "Memory" << std::endl;
						break;
					case cnc::comm::MODE::MDI:
						std::cout << "MDI" << std::endl;
						break;
					case cnc::comm::MODE::FILE_MANAGER:
						std::cout << "File Manager" << std::endl;
						break;
					case cnc::comm::MODE::EDITOR:
						std::cout << "Editor" << std::endl;
						break;
					case cnc::comm::MODE::HANDWHEEL:
						std::cout << "Handwheel" << std::endl;
						break;
					case cnc::comm::MODE::JOG:
						std::cout << "Jog" << std::endl;
						break;
					case cnc::comm::MODE::RAPID:
						std::cout << "Rapid" << std::endl;
						break;
					case cnc::comm::MODE::HOME:
						std::cout << "Home" << std::endl;
						break;
					}
					break;
				case cnc::comm::OPTION::ID:
					switch (kbdCmd.getValue()) {
					case cnc::comm::OPTION::SINGLE_BLOCK_OFF:
						std::cout << "Single Block Off" << std::endl;
						break;
					case cnc::comm::OPTION::SINGLE_BLOCK_ON:
						std::cout << "Single Block On" << std::endl;
						break;
					case cnc::comm::OPTION::OPTIONAL_STOP_OFF:
						std::cout << "Optional Stop Off" << std::endl;
						break;
					case cnc::comm::OPTION::OPTIONAL_STOP_ON:
						std::cout << "Optional Stop On" << std::endl;
						break;
					case cnc::comm::OPTION::BLOCK_SKIP_OFF:
						std::cout << "Block Skip Off" << std::endl;
						break;
					case cnc::comm::OPTION::BLOCK_SKIP_ON:
						std::cout << "Block Skip On" << std::endl;
						break;
					case cnc::comm::OPTION::DRY_RUN_OFF:
						std::cout << "Dry Run Off" << std::endl;
						break;
					case cnc::comm::OPTION::DRY_RUN_ON:
						std::cout << "Dry Run On" << std::endl;
						break;
					case cnc::comm::OPTION::MST_LOCK_OFF:
						std::cout << "MST Lock Off" << std::endl;
						break;
					case cnc::comm::OPTION::MST_LOCK_ON:
						std::cout << "MST Lock On" << std::endl;
						break;
					case cnc::comm::OPTION::MACHINE_LOCK_OFF:
						std::cout << "Machine Lock Off" << std::endl;
						break;
					case cnc::comm::OPTION::MACHINE_LOCK_ON:
						std::cout << "Machine Lock On" << std::endl;
						break;
					}
					break;
				case cnc::comm::FEEDRATE_OVERRIDE::ID:
					std::cout << kbdCmd.getValue() << "%" << std::endl;
					break;
				case cnc::comm::JOG_FEEDRATE::ID:
					std::cout << kbdCmd.getValue() << "mm/min (in/min)" << std::endl;
					break;
				case cnc::comm::RAPID_FEEDRATE_OVERRIDE::ID:
					std::cout << kbdCmd.getValue() << "%" << std::endl;
					break;
				case cnc::comm::AXIS::ID:
					switch (kbdCmd.getValue()) {
					case cnc::comm::AXIS::X_POSITIVE_IGNORE:
						std::cout << "X Axis Positive Ignore" << std::endl;
						break;
					case cnc::comm::AXIS::X_POSITIVE_SELECT:
						std::cout << "X Axis Positive Select" << std::endl;
						break;
					case cnc::comm::AXIS::X_NEGATIVE_IGNORE:
						std::cout << "X Axis Negative Ignore" << std::endl;
						break;
					case cnc::comm::AXIS::X_NEGATIVE_SELECT:
						std::cout << "X Axis Negative Select" << std::endl;
						break;
					case cnc::comm::AXIS::Y_POSITIVE_IGNORE:
						std::cout << "Y Axis Positive Ignore" << std::endl;
						break;
					case cnc::comm::AXIS::Y_POSITIVE_SELECT:
						std::cout << "Y Axis Positive Select" << std::endl;
						break;
					case cnc::comm::AXIS::Y_NEGATIVE_IGNORE:
						std::cout << "Y Axis Negative Ignore" << std::endl;
						break;
					case cnc::comm::AXIS::Y_NEGATIVE_SELECT:
						std::cout << "Y Axis Negative Select" << std::endl;
						break;
					case cnc::comm::AXIS::Z_POSITIVE_IGNORE:
						std::cout << "Z Axis Positive Ignore" << std::endl;
						break;
					case cnc::comm::AXIS::Z_POSITIVE_SELECT:
						std::cout << "Z Axis Positive Select" << std::endl;
						break;
					case cnc::comm::AXIS::Z_NEGATIVE_IGNORE:
						std::cout << "Z Axis Negative Ignore" << std::endl;
						break;
					case cnc::comm::AXIS::Z_NEGATIVE_SELECT:
						std::cout << "Z Axis Negative Select" << std::endl;
						break;
					}
					break;
				case cnc::comm::SPINDLE::ID:
					switch (kbdCmd.getValue()) {
					case cnc::comm::SPINDLE::NORMAL_ROTATION:
						std::cout << "Normal Rotation" << std::endl;
						break;
					case cnc::comm::SPINDLE::REVERSE_ROTATION:
						std::cout << "Reverse Rotation" << std::endl;
						break;
					case cnc::comm::SPINDLE::START:
						std::cout << "Start" << std::endl;
						break;
					case cnc::comm::SPINDLE::STOP:
						std::cout << "Stop" << std::endl;
						break;
					}
					break;
				case cnc::comm::SPINDLE_SPEED::ID:
					std::cout << kbdCmd.getValue() << "rpm" << std::endl;
					break;
				case cnc::comm::SPINDLE_OVERRIDE::ID:
					std::cout << kbdCmd.getValue() << "%" << std::endl;
					break;
				case cnc::comm::HANDWHEEL::ID:
					switch (kbdCmd.getValue()) {
					case cnc::comm::HANDWHEEL::FACTOR_1:
						std::cout << "x1" << std::endl;
						break;
					case cnc::comm::HANDWHEEL::FACTOR_10:
						std::cout << "x10" << std::endl;
						break;
					case cnc::comm::HANDWHEEL::FACTOR_100:
						std::cout << "x100" << std::endl;
						break;
					case cnc::comm::HANDWHEEL::DECREASE:
						std::cout << "Decrease" << std::endl;
						break;
					case cnc::comm::HANDWHEEL::INCREASE:
						std::cout << "Increase" << std::endl;
						break;
					case cnc::comm::HANDWHEEL::SELECT_AXIS_X:
						std::cout << "Select X Axis" << std::endl;
						break;
					case cnc::comm::HANDWHEEL::SELECT_AXIS_Y:
						std::cout << "Select Y Axis" << std::endl;
						break;
					case cnc::comm::HANDWHEEL::SELECT_AXIS_Z:
						std::cout << "Select Z Axis" << std::endl;
						break;
					}
					break;
				case cnc::comm::TOOL::ID:
					std::cout << kbdCmd.getValue() << std::endl;
					break;
				case cnc::comm::DEVICE::ID:
					std::cout << (kbdCmd.getValue() >> 4) << " ";
					switch (kbdCmd.getValue() & 0x0F) {
					case 0x00:
						std::cout << "Disabled" << std::endl;
						break;
					case 0x01:
						std::cout << "Enabled" << std::endl;
						break;
					case 0x02:
						std::cout << "Automatic" << std::endl;
						break;
					default:
						std::cout << "Error: " << (kbdCmd.getValue() & 0x0F) << std::endl;
					}
					break;
				}
			}
		#endif
		};

	} // namespace base

} // namespace cnc

#endif
